package com.insight.common.util;

import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;

import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;

/**
 * T019: SHA-256 哈希工具类
 * 用于计算表结构指纹，支持差异检测
 */
@Slf4j
public class HashUtil {

    /**
     * 计算字符串的 SHA-256 哈希值
     *
     * @param input 输入字符串
     * @return 64位十六进制哈希值
     */
    public static String sha256(String input) {
        if (input == null || input.isEmpty()) {
            return DigestUtils.sha256Hex("");
        }
        return DigestUtils.sha256Hex(input.getBytes(StandardCharsets.UTF_8));
    }

    /**
     * 计算字节数组的 SHA-256 哈希值
     *
     * @param input 输入字节数组
     * @return 64位十六进制哈希值
     */
    public static String sha256(byte[] input) {
        if (input == null || input.length == 0) {
            return DigestUtils.sha256Hex(new byte[0]);
        }
        return DigestUtils.sha256Hex(input);
    }

    /**
     * 计算对象的 SHA-256 哈希值（通过JSON序列化）
     *
     * @param object 任意对象
     * @return 64位十六进制哈希值
     */
    public static String sha256Object(Object object) {
        if (object == null) {
            return sha256("null");
        }
        String json = JSON.toJSONString(object);
        return sha256(json);
    }

    /**
     * 计算表结构哈希值（用于差异检测）
     * <p>
     * 包含字段：
     * - 表名
     * - 列名列表
     * - 列类型列表
     * - 主键列表
     * - 索引列表
     * </p>
     *
     * @param tableName   表名
     * @param columns     列信息列表（Map包含name, type, nullable等）
     * @param primaryKeys 主键列表
     * @param indexes     索引列表
     * @return 64位十六进制哈希值
     */
    public static String calculateTableStructureHash(
            String tableName,
            List<Map<String, Object>> columns,
            List<String> primaryKeys,
            List<Map<String, Object>> indexes) {

        // 构造结构化数据（使用TreeMap保证字段顺序一致）
        Map<String, Object> structure = new TreeMap<>();
        structure.put("tableName", tableName);

        // 列信息标准化（排序后计算哈希）
        List<String> columnSignatures = columns.stream()
                .sorted((c1, c2) -> String.valueOf(c1.get("name"))
                        .compareTo(String.valueOf(c2.get("name"))))
                .map(col -> String.format("%s:%s:%s",
                        col.get("name"),
                        col.get("type"),
                        col.get("nullable")))
                .collect(Collectors.toList());
        structure.put("columns", columnSignatures);

        // 主键标准化（排序）
        List<String> sortedPrimaryKeys = primaryKeys.stream()
                .sorted()
                .collect(Collectors.toList());
        structure.put("primaryKeys", sortedPrimaryKeys);

        // 索引标准化（排序）
        List<String> indexSignatures = indexes.stream()
                .sorted((i1, i2) -> String.valueOf(i1.get("name"))
                        .compareTo(String.valueOf(i2.get("name"))))
                .map(idx -> String.format("%s:%s",
                        idx.get("name"),
                        idx.get("columns")))
                .collect(Collectors.toList());
        structure.put("indexes", indexSignatures);

        return sha256Object(structure);
    }

    /**
     * 计算简化的表结构哈希（仅表名和列名）
     * 用于快速相似度比较
     *
     * @param tableName   表名
     * @param columnNames 列名列表
     * @return 64位十六进制哈希值
     */
    public static String calculateSimpleTableHash(String tableName, Collection<String> columnNames) {
        Map<String, Object> structure = new TreeMap<>();
        structure.put("tableName", tableName);

        List<String> sortedColumns = columnNames.stream()
                .sorted()
                .collect(Collectors.toList());
        structure.put("columns", sortedColumns);

        return sha256Object(structure);
    }

    /**
     * 计算MD5哈希值（用于快速校验，不推荐用于安全场景）
     *
     * @param input 输入字符串
     * @return 32位十六进制哈希值
     */
    public static String md5(String input) {
        if (input == null || input.isEmpty()) {
            return DigestUtils.md5Hex("");
        }
        return DigestUtils.md5Hex(input.getBytes(StandardCharsets.UTF_8));
    }

    /**
     * 比较两个哈希值是否相等
     *
     * @param hash1 哈希值1
     * @param hash2 哈希值2
     * @return true 如果相等
     */
    public static boolean equals(String hash1, String hash2) {
        if (hash1 == null || hash2 == null) {
            return false;
        }
        return hash1.equalsIgnoreCase(hash2);
    }

    /**
     * 验证哈希值格式是否正确（SHA-256应为64位十六进制）
     *
     * @param hash 待验证的哈希值
     * @return true 如果格式正确
     */
    public static boolean isValidSha256(String hash) {
        if (hash == null || hash.isEmpty()) {
            return false;
        }
        return hash.matches("^[a-fA-F0-9]{64}$");
    }

    /**
     * 验证哈希值格式是否正确（MD5应为32位十六进制）
     *
     * @param hash 待验证的哈希值
     * @return true 如果格式正确
     */
    public static boolean isValidMd5(String hash) {
        if (hash == null || hash.isEmpty()) {
            return false;
        }
        return hash.matches("^[a-fA-F0-9]{32}$");
    }

    /**
     * 私有构造函数，防止实例化
     */
    private HashUtil() {
        throw new UnsupportedOperationException("Utility class cannot be instantiated");
    }
}
